package org.yn.car.service.impl;

import com.alibaba.excel.util.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.yn.base.config.HighlightResultMapper;
import org.yn.base.util.PageInfo;
import org.yn.base.util.SearchUtil;
import org.yn.car.doc.CarDoc;
import org.yn.car.query.CarSearchQuery;
import org.yn.car.repository.CarDocRepository;
import org.yn.car.service.ICarSearchService;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
public class CarSearchServiceImpl implements ICarSearchService {

    @Autowired
    ElasticsearchTemplate elasticsearchTemplate;
    @Autowired
    HighlightResultMapper highlightResultMapper;
    @Autowired
  private   CarDocRepository carDocRepository;

    @Override
    public PageInfo<CarDoc> search(CarSearchQuery query) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //前端的es查询

        // 高级查询  -- 条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        List<QueryBuilder> must = boolQuery.must();
        List<QueryBuilder> filter = boolQuery.filter();






        
        // 关键字查询  keyword   并且分词查询，
        // 先判断keyword 是否为空 ,      QueryBuilders是工具类
        if (StringUtils.isNotBlank(query.getKeyword())){//不为空,分词查询
            must.add(QueryBuilders.multiMatchQuery(query.getKeyword(),"title","typeName","shopName","carInfo"));
        }


        //类型查询   typeId
        if (Objects.nonNull(query.getTypeId())){     //先判断是否空 ，不为空给 filter添加查询条件
            filter.add(QueryBuilders.termQuery("typeId",query.getTypeId())); //termQuery字段查询
        }

        //价格 范围查询
        if (Objects.nonNull(query.getMinPrice())){      //最低价格
            filter.add(QueryBuilders.rangeQuery("costprice").gte(query.getMinPrice()));//gte 大于等于
        }

        if (Objects.nonNull(query.getMaxPrice())){      //最高价格
            filter.add(QueryBuilders.rangeQuery("costprice").lt(query.getMaxPrice()));  //小于等于
        }


        //车龄查询  --当前时间-X年 得到一个时间节点,然后车辆的reigstertime和这个节点相比较
        // 5年以上  5年以下
        // 先计算出carAge所对应的时间点  当前时间-carAge
        // 以上  reigstertime<2018/  以内  reigstertime>2018/
        if(Objects.nonNull(query.getCarAge())){
            // 根据车龄计算要比较的时间节点
            long time = DateUtils.addYears(new Date(), 0 - query.getCarAge()).getTime();   //加一个负数 就相当于减
            if(query.getCarAgeType() == 1){ // XX年以上
                filter.add(QueryBuilders.rangeQuery("reigstertime").lt(time));
            }else{     //xx年以内
                // 以内,就是说比时间节点要大的时间
                filter.add(QueryBuilders.rangeQuery("reigstertime").gte(time));
            }
        }


        //经纬度距离查询

//       if(Objects.nonNull(query.getLat()) && Objects.nonNull(query.getLon()) && Objects.nonNull(query.getDistance())){
//        GeoDistanceQueryBuilder shopPoint = QueryBuilders.geoDistanceQuery("shopPoint");
//        // 设置要进行查询地点的经纬度设置
//        shopPoint.point(query.getLat(),query.getLon());
//        // 设置要查询的范围
//        shopPoint.distance(query.getDistance(), DistanceUnit.KILOMETERS);
//        filter.add(shopPoint);
//        }



        //其他字段查询,,    costeffective   rushsale    quasinewcar    transitivecountry
        if(Objects.nonNull(query.getCosteffective())){
            filter.add(QueryBuilders.termQuery("costeffective",query.getCosteffective()));
        }
        if(Objects.nonNull(query.getRushsale())){
            filter.add(QueryBuilders.termQuery("rushsale",query.getRushsale()));
        }
        if(Objects.nonNull(query.getQuasinewcar())){
            filter.add(QueryBuilders.termQuery("quasinewcar",query.getQuasinewcar()));
        }
        if(Objects.nonNull(query.getTransitivecountry())){
            filter.add(QueryBuilders.termQuery("transitivecountry",query.getTransitivecountry()));
        }

        //-------------------------------
        queryBuilder.withQuery(boolQuery);

        //排序
        SortOrder sortOrder = SortOrder.ASC;    //4. 默认按照 asc正序排序
        if("desc".equals(query.getSortType())){
            sortOrder = SortOrder.DESC;
        }

        if (StringUtils.isNotBlank(query.getSortField())){       //1.如果前端传入的排序字段不为空
           SortBuilder order = SortBuilders.fieldSort(query.getSortField()).order(sortOrder);//3.  fieldSort, order
            queryBuilder.withSort(order);                            // 2.传入sortBuilder 参数
        }else {
            // 距离排序字段,字段必须是point类型的才可以
            // 前端传入的经纬度
            GeoDistanceSortBuilder sortBuilder = SortBuilders.geoDistanceSort("shopPoint", query.getLat(), query.getLon());
            sortBuilder.order(sortOrder);
            queryBuilder.withSort(sortBuilder);
        }

        //高亮查询
        HighlightBuilder.Field field1 = new HighlightBuilder.Field("title")
                .preTags("<span style='color:red'>").postTags("</span>");
        HighlightBuilder.Field field2 = new HighlightBuilder.Field("typeName")
                .preTags("<span style='color:red'>").postTags("</span>");
        HighlightBuilder.Field field3 = new HighlightBuilder.Field("shopName")
                .preTags("<span style='color:red'>").postTags("</span>");
        HighlightBuilder.Field field4 = new HighlightBuilder.Field("carInfo")
                .preTags("<span style='color:red'>").postTags("</span>");

        //将要查询的 放入builder
        queryBuilder.withHighlightFields(field1,field2,field3,field4);

        //分页查询
        TermsAggregationBuilder typeIdGroup = AggregationBuilders.terms("typeIdGroup").field("typeId").order(BucketOrder.count(true))
                .subAggregation(
                        AggregationBuilders.terms("typeNameGroup").field("typeName.keyword").order(BucketOrder.count(true))
                );

        TermsAggregationBuilder shopIdGroup = AggregationBuilders.terms("shopIdGroup").field("shopId").order(BucketOrder.count(true))
                .subAggregation(
                        AggregationBuilders.terms("shopNameGroup").field("shopName.keyword").order(BucketOrder.count(true))
                );

        queryBuilder.addAggregation(typeIdGroup);
        queryBuilder.addAggregation(shopIdGroup);

        //分页查询
        // PageRequest  第一个参数,是你当前要查询的页数-1   这里面从0开始算的
        queryBuilder.withPageable(PageRequest.of(query.getCurrentPage()-1,query.getPageSize()));

        // 查询es
        //高亮结果映射器
                 //carDocRepository 没有自义定的结果映射器
        //所以用elasticsearchTemplate
//        Page<CarDoc> docPage = carDocRepository.search(queryBuilder.build());

        //聚合
        AggregatedPage<CarDoc> docPage =  elasticsearchTemplate.queryForPage(queryBuilder.build(),CarDoc.class,highlightResultMapper);



        // 将docPage中的聚合结果转为map,然后将map给PageInfo返回给前端就可以了
        Aggregations aggregations = docPage.getAggregations();
        Map<String, Object> map = SearchUtil.handleTermsAggsData(aggregations);


        // 将es的分页对象转为我们自己的分页对象,返回给前端
        long total = docPage.getTotalElements();
        List<CarDoc> rows = docPage.getContent();

        // 将返回的数据做参数转换 地址
        for (CarDoc row : rows) {
            String shopAddress = row.getShopAddress();
            shopAddress = shopAddress.split("区")[0];
            row.setShopAddress(shopAddress+"区");
        }

        return new PageInfo<CarDoc>((int)total,rows,map);
    }
}
